﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;
using System.IO;
using System.Data.Common;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Checksums;
using System.Text;
using System.Threading;
using ResourceBlender.DAL;

/// <summary>
/// Allows users to backup or restore the database ASynchronously
/// </summary>
public partial class Backup : LocalPage
{
    protected void Page_Load(object sender, EventArgs e)
    {
        lblRestoreError.Visible = false;
    }
    protected void btnBackup_Click(object sender, EventArgs e) {
        if(!chkBakApplications.Checked && !chkBakElements.Checked && !chkBakResources.Checked && !chkBakUsers.Checked && !chkBakTemplates.Checked) {
            lblNoBackupOptions.Visible = true;
            return;
        }
        ResourceGenerator.GenerateXML(true, chkBakApplications.Checked, chkBakElements.Checked, chkBakResources.Checked, chkBakTemplates.Checked, chkBakUsers.Checked);
    }

    /// <summary>
    /// Detects the type (zip/xml) of uploaded file and parses it to an XmlDataDocument
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void btnRestore_Click(object sender, EventArgs e) {
        if(!uplBackupfile.HasFile) {
            lblNoFileSelected.Visible = true;
            return;
        } else if(!chkResApplications.Checked && !chkResElements.Checked && !chkResResources.Checked && !chkResUsers.Checked && !chkResTemplates.Checked) {
            lblRestoreError.Text = Resources.UIStrings.NothingSelectedToRestore;
            lblRestoreError.Visible = true;
            return;
        }
        try {
            XmlDataDocument doc = new XmlDataDocument();
            string ext = Path.GetExtension(uplBackupfile.FileName).ToLowerInvariant();
            switch(ext) {
                case ".xml":
                    doc.Load(uplBackupfile.FileContent);
                    break;
                case ".zip":
                    using(MemoryStream ms = new MemoryStream()) {
                        ZipInputStream zipStream = new ZipInputStream(uplBackupfile.FileContent);
                        ZipEntry entry = zipStream.GetNextEntry();
                        byte[] buffer = new byte[1024];
                        int size;
                        while((size = zipStream.Read(buffer, 0, buffer.Length)) > 0) {
                            ms.Write(buffer, 0, size);
                        }
                        ms.Seek(0, SeekOrigin.Begin);
                        doc.Load(ms);
                    }
                    break;
                default:
                    throw new ArgumentException(string.Format(Resources.Errors.UnsupportedFileType, ext));
            }
            Session["restoreFile"] = doc;
            lstProgress.Items.Add(string.Format(Resources.UIStrings.PromptStartUploadedFile, Resources.UIStrings.Backup, Resources.UIStrings.Restore));
            mltMain.ActiveViewIndex = 1;
        } catch(Exception ex) {
            lblRestoreError.Text = string.Format(Resources.Errors.ErrorNumDescription,"406", ex.Message);
            lblRestoreError.Visible = true;
        }
    }

    /// <summary>
    /// Starts a background thread and enables a timer to show the status of the restore operation.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void btnStartRestore_Click(object sender, EventArgs e) {
        XmlDataDocument doc = Session["restoreFile"] as XmlDataDocument;
        if(doc == null) {
            lblRestoreError.Text = string.Format(Resources.Errors.ErrorNumDescription, "408", Resources.Errors.LoadFileCacheFail);
            lblRestoreError.Visible = true;
            return;
        }
        lstProgress.Items.Clear();
        Thread worker = new Thread(new ThreadStart(StartRestore));
        imgProgress.Visible = true;
        worker.Name = "RestoreWorker";
        worker.IsBackground = true;
        worker.Start();
        Session["worker"] = worker;
        List<ListItem> log = new List<ListItem>();
        log.Add(new ListItem(Resources.UIStrings.Starting)); // the log is deserialized on every timer tick
        Session["log"] = log;
        Session["restoreDone"] = bool.FalseString;
        tmrRestore.Enabled = true;
        btnStartRestore.Enabled = false;
    }

    /// <summary>
    /// Starts the restoration, logging progress to Session["log"].
    /// </summary>
    private void StartRestore() {
        XmlDataDocument doc = Session["restoreFile"] as XmlDataDocument;
        try {
            ResourceBlender.DAL.Database db = new ResourceBlender.DAL.Database();
            db.OpenConnection();
            if(chkClearDatabase.Checked) {
                try {
                    db.DropTables(chkResApplications.Checked, chkResElements.Checked, chkResResources.Checked, chkResResources.Checked, chkResUsers.Checked, chkResTemplates.Checked);
                } catch { }
                db.CreateTables(chkResApplications.Checked, chkResElements.Checked, chkResResources.Checked, chkResResources.Checked, chkResUsers.Checked, chkResTemplates.Checked);
            }
            StatusMessage.StatusChangedEventHandler logHandler = new StatusMessage.StatusChangedEventHandler((statusMessage) => {
                LogText(statusMessage.Message, statusMessage.MessageType == StatusMessage.StatusMessageType.Error);
            });
            ResourceImporter.ImportXMLBackup(logHandler, doc, chkClearDatabase.Checked, chkResApplications.Checked, chkResElements.Checked, chkResResources.Checked, chkResUsers.Checked, chkResTemplates.Checked); 
            Session["restoreDone"] = bool.TrueString;
        } catch(Exception ex) {
            LogText(string.Format(Resources.Errors.ErrorNumDescription, "408", ex.Message), true);
            Session["restoreDone"] = bool.TrueString;
        }
        Thread worker = Session["worker"] as Thread;
        if(worker != null) {
            worker.Abort();
        }
    }

    /// <summary>
    /// Logs text to the progress BulletedList.
    /// </summary>
    /// <param name="text">The text to log.</param>
    /// <param name="error"><c>true</c> if the 'error' CSS class should be applied to the text.</param>
    private void LogText(string text, bool error) {
        List<ListItem> log = Session["log"] as List<ListItem>;
        ListItem itm = new ListItem(text);
        if(error) {
            itm.Attributes.Add("class", "error");
        }
        log.Add(itm);
    }

    /// <summary>
    /// Deserializes the restore log from the Session cache and re-populates the progress BulletedList.
    /// Detects completion of the operation and stops the timer.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void tmrRestore_Tick(object sender, EventArgs e) {
        try {
            List<ListItem> log = Session["log"] as List<ListItem>;
            lstProgress.Items.Clear();
            if(log == null) {
                log = new List<ListItem>();
            } else {
                lstProgress.Items.AddRange(log.ToArray());
            }
            if(bool.Parse(Session["restoreDone"].ToString())) {
                (sender as System.Web.UI.Timer).Enabled = false;
                Session.Remove("restoreFile");
                Session.Remove("restoreDone");
                imgProgress.Visible = false;
            }
        } catch {
            lstProgress.Items.Add(Resources.Errors.ErrorCommunicateServerAborting);
            Session.Remove("restoreFile");
            Session["restoreDone"] = bool.TrueString;
        }
    }
}
